package io.github.siltal.examsystembackend.service.impl;

import io.github.siltal.examsystembackend.dto.*;
import io.github.siltal.examsystembackend.entity.User;
import io.github.siltal.examsystembackend.repository.UserRepository;
import io.github.siltal.examsystembackend.service.UserService;
import org.springframework.stereotype.Service;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.util.List;
import java.util.Optional;
import java.util.regex.Pattern;

@Service
public class UserServiceImpl implements UserService {
    private final PasswordEncoder passwordEncoder;
    private final UserRepository userRepository;
    private static final Pattern EMAIL_PATTERN = Pattern.compile(
            "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,6}$"
    );


    public UserServiceImpl(PasswordEncoder passwordEncoder, UserRepository userRepository) {
        this.passwordEncoder = passwordEncoder;
        this.userRepository = userRepository;
    }


    @Override
    public RegisterResponse createUser(RegisterRequest registerRequest) {
         if (registerRequest.username().isEmpty() || registerRequest.password().isEmpty()) {
            return RegisterResponse.UsernameOrPasswordCantBeEmpty();
        }
        if (registerRequest.username().length() < 5 || registerRequest.username().length() > 16) {
            return RegisterResponse.UsernameLengthError();
        }
        if (registerRequest.password().length() < 8 || registerRequest.password().length() > 20) {
            return RegisterResponse.PasswordLengthError();
        }

        if (userRepository.existsByUsername(registerRequest.username())) {
            return RegisterResponse.usernameConflict();
        }

        // 3. If it doesn't exist, proceed with user creation
        User newUser = new User(registerRequest);
        newUser.setPassword(passwordEncoder.encode(newUser.getPassword()));
        User savedUser = userRepository.save(newUser);

        // 4. Return a success DTO containing the newly saved user
        return RegisterResponse.from(savedUser);
    }

    @Override
    public LoginResponse login(LoginRequest loginRequest) {
        if (loginRequest.username() == null || loginRequest.password() == null ||
                loginRequest.username().isEmpty() || loginRequest.password().isEmpty()) {
            return LoginResponse.usernameOrPasswordCantBeEmpty();
        }

        Optional<User> userOpt = userRepository.findByUsername(loginRequest.username());

        if (userOpt.isEmpty()) {
            return LoginResponse.userNotFound();
        }
        User user = userOpt.get();
        if (!passwordEncoder.matches(loginRequest.password(), user.getPassword())) {
            return LoginResponse.usernameOrPasswordIncorrect();
        }
        return LoginResponse.from(user);
    }


    @Override
    public List<User> getAllUsers() {
        return userRepository.findAll();
    }

    @Override
    public Optional<User> getUserById(Long id) {
        return userRepository.findById(id);
    }

    @Override
    public Optional<User> updateUser(Long id, User userDetails) {
        return userRepository.findById(id).map(existingUser -> {
            existingUser.setUsername(userDetails.getUsername());
            existingUser.setEmail(userDetails.getEmail());
            existingUser.setPassword(passwordEncoder.encode(userDetails.getPassword()));
            return userRepository.save(existingUser);
        });
    }

    @Override
    public boolean deleteUser(Long id) {
        return userRepository.findById(id).map(user -> {
            userRepository.delete(user);
            return true;
        }).orElse(false);
    }


    @Override
    public PasswordChangeResponse passwordChange(PasswordChangeRequest passwordChangeRequest) {
        Optional<User> byId = this.userRepository.findById(passwordChangeRequest.id());
        if (byId.isEmpty()) {
            return PasswordChangeResponse.userNotFound();
        }
        User user = byId.get();
        if (!passwordEncoder.matches(passwordChangeRequest.oldPassword(), user.getPassword())) {
            return PasswordChangeResponse.passwordIncorrect();
        }
        if (passwordChangeRequest.newPassword().length() < 8 || passwordChangeRequest.newPassword().length() > 20) {
            return PasswordChangeResponse.PasswordLengthError();
        }
        user.setPassword(passwordEncoder.encode(passwordChangeRequest.newPassword()));
        userRepository.save(user);
        return PasswordChangeResponse.success();
    }

    @Override
    public EmailChangeResponse emailChange(EmailChangeRequest emailChangeRequest) {
        Optional<User> byId = this.userRepository.findById(emailChangeRequest.id());
        if (byId.isEmpty()) {
            return EmailChangeResponse.userNotFound();
        }
        User user = byId.get();
        String newEmail = user.getEmail();
        // format check
        if (newEmail == null || newEmail.isBlank() || !EMAIL_PATTERN.matcher(newEmail).matches()) {
            return EmailChangeResponse.emailFormatError();
        }
        user.setEmail(emailChangeRequest.newEmail());
        userRepository.save(user);
        return EmailChangeResponse.success();
    }
}